package top.zhangjianyong.tools.indicator.strategy.impl;

import org.springframework.stereotype.Component;
import top.zhangjianyong.tools.entity.ETFData;
import top.zhangjianyong.tools.indicator.context.IndicatorContext;
import top.zhangjianyong.tools.indicator.model.TechnicalIndicatorResult;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

/**
 * 布林带（Bollinger Bands）指标计算器
 * 窗口20，上下轨2倍标准差
 *
 * @author zhangjianyong
 * @date 2024/01/01
 */
@Component
public class BollingerBandsIndicator extends AbstractIndicator {

    private static final String INDICATOR_NAME = "BOLL";
    private static final int PERIOD = 20;
    private static final int STD_DEVIATION = 2;

    @Override
    public String getName() {
        return INDICATOR_NAME;
    }

    @Override
    public int getMinDataSize() {
        return PERIOD;
    }

    @Override
    protected TechnicalIndicatorResult doCalculate(IndicatorContext context) {
        List<ETFData> dataList = context.getDataList();

        // 取最后PERIOD个数据点
        List<ETFData> subList = dataList.subList(dataList.size() - PERIOD, dataList.size());

        // 计算中轨（20日SMA）
        BigDecimal middleBand = calculateSMA(subList);

        // 计算标准差
        BigDecimal stdDev = calculateStandardDeviation(subList, middleBand);

        // 计算上轨和下轨
        BigDecimal upperBand = middleBand.add(stdDev.multiply(new BigDecimal(STD_DEVIATION)));
        BigDecimal lowerBand = middleBand.subtract(stdDev.multiply(new BigDecimal(STD_DEVIATION)));

        // 获取当前价格
        BigDecimal currentPrice = dataList.get(dataList.size() - 1).getValue();

        TechnicalIndicatorResult result = TechnicalIndicatorResult.success(getName());
        result.addValue("boll_upper", upperBand.setScale(4, RoundingMode.HALF_UP));
        result.addValue("boll_middle", middleBand.setScale(4, RoundingMode.HALF_UP));
        result.addValue("boll_lower", lowerBand.setScale(4, RoundingMode.HALF_UP));
        result.addValue("boll_width", upperBand.subtract(lowerBand).setScale(4, RoundingMode.HALF_UP));
        result.addValue("boll_position", calculatePosition(currentPrice, upperBand, lowerBand).setScale(2, RoundingMode.HALF_UP));

        return result;
    }

    /**
     * 计算SMA
     */
    private BigDecimal calculateSMA(List<ETFData> dataList) {
        BigDecimal sum = BigDecimal.ZERO;
        for (ETFData data : dataList) {
            sum = sum.add(data.getValue());
        }
        return sum.divide(new BigDecimal(dataList.size()), 4, RoundingMode.HALF_UP);
    }

    /**
     * 计算标准差
     */
    private BigDecimal calculateStandardDeviation(List<ETFData> dataList, BigDecimal mean) {
        BigDecimal sumSquaredDiff = BigDecimal.ZERO;

        for (ETFData data : dataList) {
            BigDecimal diff = data.getValue().subtract(mean);
            sumSquaredDiff = sumSquaredDiff.add(diff.multiply(diff));
        }

        BigDecimal variance = sumSquaredDiff.divide(new BigDecimal(dataList.size()), 8, RoundingMode.HALF_UP);
        // 计算标准差（平方根）
        double stdDev = Math.sqrt(variance.doubleValue());

        return BigDecimal.valueOf(stdDev).setScale(4, RoundingMode.HALF_UP);
    }

    /**
     * 计算价格在布林带中的位置（0-100）
     * 0表示在下轨，100表示在上轨
     */
    private BigDecimal calculatePosition(BigDecimal price, BigDecimal upperBand, BigDecimal lowerBand) {
        BigDecimal bandWidth = upperBand.subtract(lowerBand);
        if (bandWidth.compareTo(BigDecimal.ZERO) == 0) {
            return new BigDecimal(50); // 如果带宽为0，返回中间位置
        }

        BigDecimal position = price.subtract(lowerBand)
            .divide(bandWidth, 4, RoundingMode.HALF_UP)
            .multiply(new BigDecimal(100));

        // 限制在0-100范围内
        if (position.compareTo(BigDecimal.ZERO) < 0) {
            position = BigDecimal.ZERO;
        } else if (position.compareTo(new BigDecimal(100)) > 0) {
            position = new BigDecimal(100);
        }

        return position;
    }
}

